home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
C++
/
Frameworks
/
Sprocket Framework DR2
/
Sprocket Starter
/
SprocketStarter Code
/
TextWindow.cp
< prev
next >
Wrap
Text File
|
1996-06-15
|
52KB
|
2,261 lines
/*
File: TextWindow.cp
Project: Sample code for Sprocket Framework 1.1 (DR2), released 6/15/96
Contains: Complete WASTE based text editing window
To Do: Squash bugs, fix Copy Paste & Undo
Sprocket Major Contributors:
----------------------------
Dave Falkenburg, producer of Sprocket 1.0
Bill Hayden, producer of Sprocket 1.1
Steve Sisak, producer of the upcoming Sprocket 2.0
Pete Alexander Steve Falkenburg Randy Thelen
Eric Berdahl Nitin Ganatra Chris K. Thomas
Marshall Clow Dave Hershey Leonard Rosenthal
Tim Craycroft Dave Mark Dean Yu
David denBoer Gary Powell
Cameron Esfahani Jon Summers Apple Computer, Inc.
Comments, Additions, or Corrections:
------------------------------------
Bill Hayden, Nikol Software <nikol@codewell.com>
*/
#include "StandardMenus.h"
#include "Sprocket.h"
#include "UDialog.h"
#include "TextWindow.h"
#include "UString.h"
#include "UResources.h"
#include <ColorPicker.h>
#ifndef _LIMITS
#include "limits.h"
#endif
#ifndef __SCRAP__
#include <Scrap.h>
#endif
static pascal void TextScrolled( WEReference we );
static pascal void ScrollProc( ControlRef bar, ControlPartCode partCode );
pascal OSErr TranslateDrag( DragReference theDrag, ItemReference theItem, FlavorType requestedType, Handle dataHandle );
static short FindMenuItemText( MenuRef menu, ConstStr255Param stringToFind );
static CommandID SelectedColor(RGBColor& theColor);
static WEScrollUPP sWEScroller;
static WETranslateDragUPP sWEDragTranslator;
static ControlActionUPP sScrollProc;
static short sScrollStep; // how many pixels to scroll (used by ScrollProc)
#define kTitleHeight 20 // usual height of a window title bar
#define kTextMargin 3 // indent of text rect from a window port rect
#define kScrollDelta 11 // pixels to scroll when the scroll bar arrow is clicked
#define kTextWindowTemplateID 2000
// kMaxDocWidth is an arbitrary number used to specify the width of the WERec's
// destination rectangle so that word wrap and horizontal scrolling can be
// demonstrated.
enum {
kMinWindowWidth = 200,
kMinWindowHeight = 80,
kMaxWindowWidth = 576
};
// ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
// values for control positioning and sizing.
const short kScrollbarAdjust = 15;
MenuRef TTextWindow::fFontMenu;
MenuRef TTextWindow::fSizeMenu;
MenuRef TTextWindow::fStyleMenu;
MenuRef TTextWindow::fColorMenu;
/**********************************************************************************/
// notice that we pass the resID parameter up to our base class,
// which actually creates the window for us
TTextWindow::TTextWindow(void)
{
//fTemplateID = ResID;
fDocVScroll = fDocHScroll = nil;
fWEHandle = nil;
fToolbarHeight = 60;
SetUpStaticMenu(); // CreateWindow will call Activate(), so we must do this first
}
/**********************************************************************************/
OSErr TTextWindow::ITextWindow(FSSpecPtr filespec)
{
LongRect destRect, viewRect;
Rect boundsRect;
OSErr err;
short weFlags;
pcpy(this->fName, "\pUntitled");
if (filespec)
{
this->fBackingFile = new TFile(*filespec);
pcpy(this->fName, filespec->name);
}
else
{
this->fBackingFile = new TFile;
pcpy(this->fName, "\pUntitled");
}
if (!this->fBackingFile)
return fnfErr;
this->CreateWindow(kNormalWindow);
if (!fWindow)
return memFullErr;
SetWTitle(fWindow, fName);
SetPortWindowPort(fWindow); /* Do this first, or else */
GetWERect(viewRect);
GetWERect(destRect);
destRect.right = destRect.left + kMaxWindowWidth;
weFlags = weDoAutoScroll +
weDoUndo +
weDoIntCutAndPaste +
weDoUseTempMem +
weDoDrawOffscreen;
if (gHasDragManager)
weFlags += weDoDragAndDrop + weDoOutlineHilite;
err = WENew( &destRect, &viewRect, weFlags, &fWEHandle);
if ( err != noErr )
return err;
WESetAlignment( weFlushLeft, fWEHandle );
// save a reference to the window in the WE instance
err = WESetInfo( weRefCon, &this, fWEHandle );
if ( err != noErr )
return err;
if ( sWEScroller == NULL )
{
sWEScroller = NewWEScrollProc( TextScrolled );
if (gHasDragManager)
sWEDragTranslator = NewWETranslateDragProc( TranslateDrag );
}
// set up our callbacks
err = WESetInfo( weScrollProc, &sWEScroller, fWEHandle );
if ( err != noErr )
return err;
if (gHasDragManager)
{
err = WESetInfo( weTranslateDragHook, &sWEDragTranslator, fWEHandle );
if ( err != noErr )
return err;
}
WEActivate(fWEHandle);
// get scrollbars
boundsRect = GetContentsBounds();
LocalToGlobal((Point*) &boundsRect.top);
LocalToGlobal((Point*) &boundsRect.bottom);
boundsRect.left = boundsRect.right - kScrollbarAdjust;
boundsRect.bottom -= kScrollbarAdjust - 1;
boundsRect.top = -1 + fToolbarHeight;
boundsRect.right++;
fDocVScroll = NewControl(fWindow, &boundsRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
if (!fDocVScroll)
return 2;
boundsRect = GetContentsBounds();
LocalToGlobal((Point*) &boundsRect.top);
LocalToGlobal((Point*) &boundsRect.bottom);
boundsRect.top = boundsRect.bottom - kScrollbarAdjust;
boundsRect.right -= kScrollbarAdjust - 1;
boundsRect.left = -1;
boundsRect.bottom++;
fDocHScroll = NewControl(fWindow, &boundsRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
if (!fDocHScroll)
return 3;
// install buttons in toolbar
SetRect(&boundsRect, 10, 8, 42, 40);
fSaveButton = NewControl(fWindow, &boundsRect, "\pSave…", true, 1000, 0, 0, 609, cSave);
SetRect(&boundsRect, 150, 7, 166, 19);
fPlainButton = NewControl(fWindow, &boundsRect, "\p", true, 1010, 0, 0, 608, cPlainText);
OffsetRect(&boundsRect, 20, 0);
fBoldButton = NewControl(fWindow, &boundsRect, "\p", true, 1011, 0, 0, 608, cBold);
OffsetRect(&boundsRect, 20, 0);
fItalicButton = NewControl(fWindow, &boundsRect, "\p", true, 1012, 0, 0, 608, cItalic);
OffsetRect(&boundsRect, 20, 0);
fUnderlineButton = NewControl(fWindow, &boundsRect, "\p", true, 1013, 0, 0, 608, cUnderline);
SetRect(&boundsRect, 150, 23, 166, 35);
fLeftAlignButton = NewControl(fWindow, &boundsRect, "\p", true, 1002, 0, 0, 608, cAlignLeft);
OffsetRect(&boundsRect, 20, 0);
fCenterAlignButton = NewControl(fWindow, &boundsRect, "\p", true, 1004, 0, 0, 608, cAlignCenter);
OffsetRect(&boundsRect, 20, 0);
fRightAlignButton = NewControl(fWindow, &boundsRect, "\p", true, 1003, 0, 0, 608, cAlignRight);
OffsetRect(&boundsRect, 20, 0);
fJustifyButton = NewControl(fWindow, &boundsRect, "\p", true, 1005, 0, 0, 608, cAlignJustify);
SetRect(&boundsRect, 150, 39, 166, 51);
fBlackButton = NewControl(fWindow, &boundsRect, "\p", true, 1020, 0, 0, 608, cBlack);
OffsetRect(&boundsRect, 20, 0);
fBlueButton = NewControl(fWindow, &boundsRect, "\p", true, 1021, 0, 0, 608, cBlue);
OffsetRect(&boundsRect, 20, 0);
fGreenButton = NewControl(fWindow, &boundsRect, "\p", true, 1022, 0, 0, 608, cGreen);
OffsetRect(&boundsRect, 20, 0);
fRedButton = NewControl(fWindow, &boundsRect, "\p", true, 1023, 0, 0, 608, cRed);
OffsetRect(&boundsRect, 20, 0);
fPinkButton = NewControl(fWindow, &boundsRect, "\p", true, 1024, 0, 0, 608, cPink);
OffsetRect(&boundsRect, 20, 0);
fOrangeButton = NewControl(fWindow, &boundsRect, "\p", true, 1025, 0, 0, 608, cOrange);
OffsetRect(&boundsRect, 20, 0);
fBrownButton = NewControl(fWindow, &boundsRect, "\p", true, 1026, 0, 0, 608, cBrown);
err = LCAttach( fDocVScroll );
if ( err != noErr )
return err;
err = LCAttach( fDocHScroll );
if ( err != noErr )
return err;
this->ViewChanged();
this->AdjustBars();
this->ScrollBarChanged();
ShowWindow(fWindow);
this->Activate(true);
this->Draw();
return noErr;
}
/**********************************************************************************/
TTextWindow::~TTextWindow()
{
if ( fWEHandle != nil )
{
WEDispose(fWEHandle); // dispose the WEHandle if we got far enough to make one
}
delete this->fBackingFile;
SetCursor(&qd.arrow); // if the user closed the window while in the text area
}
/**********************************************************************************/
Boolean TTextWindow::Close(void)
{
Boolean retVal;
if ( fDocVScroll != nil )
{
LCDetach(fDocVScroll);
}
if ( fDocHScroll != nil )
{
LCDetach(fDocHScroll);
}
retVal = TWindow::Close();
if ( retVal ) // in case we are the last TTextWindow open, disable all the edit commands
{
gMenuBar->EnableCommand ( cUndo, false );
gMenuBar->EnableCommand ( cCut, false );
gMenuBar->EnableCommand ( cCopy, false );
gMenuBar->EnableCommand ( cPaste, false );
gMenuBar->EnableCommand ( cClear, false );
gMenuBar->EnableCommand ( cSelectAll, false );
}
return retVal;
}
/**********************************************************************************/
WindowRef TTextWindow::MakeNewWindow(WindowRef behindWindow)
{
return GetNewColorOrBlackAndWhiteWindow( kTextWindowTemplateID, nil, behindWindow );
}
/**********************************************************************************/
// Called when a mouseDown occurs in the grow box of an active window.
void TTextWindow::AdjustForNewWindowSize(Rect* oldRect, Rect* newRect)
{
GrafPtr savePort;
Rect r;
RgnHandle tempRgn, dirtyRgn;
GetPort( &savePort );
SetPortWindowPort( fWindow );
// create temporarty regions for calculations
tempRgn = NewRgn();
dirtyRgn = NewRgn();
// save old text region
GetTextRect( &r );
RectRgn( tempRgn, &r );
// erase the old grow icon rect
SetRect(&r, oldRect->right - 15, oldRect->bottom - 15, oldRect->right, oldRect->bottom);
InvalRect( &r );
// hide the scroll bars
HideControl( fDocVScroll );
HideControl( fDocHScroll );
// perform the actual resizing of the window, redraw scroll bars and grow icon
this->ViewChanged();
// calculate the dirty region (to be updated)
SetRect(&r, oldRect->left, oldRect->top, oldRect->right - kScrollbarWidth - kTextMargin,
oldRect->bottom - kScrollbarWidth - kTextMargin);
RectRgn( tempRgn, &r );
RectRgn( dirtyRgn, newRect );
DiffRgn(dirtyRgn, tempRgn, dirtyRgn);
// mark the dirty region as invalid
InvalRgn( dirtyRgn );
GetTextRect( &r );
RectRgn( tempRgn, &r );
InsetRect(&r, -kTextMargin, -kTextMargin);
RectRgn( dirtyRgn, &r );
DiffRgn(dirtyRgn, tempRgn, dirtyRgn);
EraseRgn(dirtyRgn);
// throw away temporary regions
DisposeRgn( tempRgn );
DisposeRgn( dirtyRgn );
SetPort( savePort );
}
/**********************************************************************************/
void TTextWindow::ViewChanged( void )
{
GrafPtr savePort;
ControlRef bar;
Rect r;
LongRect viewRect;
GetPort( &savePort );
SetPortWindowPort( fWindow );
// resize the text area
GetTextRect( &r );
WERectToLongRect( &r, &viewRect );
WESetViewRect( &viewRect, fWEHandle );
// move and resize the control bars
// first, the vertical bar
bar = fDocVScroll;
GetScrollBarRect( v, &r );
MoveControl( bar, r.left, r.top );
SizeControl( bar, r.right - r.left, r.bottom - r.top );
ValidRect( &r );
// now the horizontal bar
bar = fDocHScroll;
GetScrollBarRect( h, &r );
MoveControl( bar, r.left, r.top );
SizeControl( bar, r.right - r.left, r.bottom - r.top );
ValidRect( &r );
// reset the thumb positions and the max values of the control bars
this->AdjustBars();
// redraw the control bars
ShowControl( fDocVScroll );
ShowControl( fDocHScroll );
// redraw the grow icon
DrawJustTheGrowIcon(fWindow);
SetPort( savePort );
}
/**********************************************************************************/
void TTextWindow::AdjustBars(void)
{
GrafPtr savePort;
LongRect viewRect, destRect;
long value;
long max;
ControlRef bar;
GetPort( &savePort );
SetPortWindowPort( fWindow );
// get the view and destination rectangle
WEGetViewRect( &viewRect, fWEHandle );
WEGetDestRect( &destRect, fWEHandle );
// do the vertical axis
// get scroll bar handle
bar = fDocVScroll;
// calculate new scroll bar settings
// NOTE: (destRect.bottom - destRect.top) always equals the total text height because
// WASTE automatically updates destRect.bottom whenever line breaks are recalculated
value = viewRect.top - destRect.top;
max = value + (destRect.bottom - viewRect.bottom);
// make sure max is always non-negative
if ( max <= 0 )
max = 0;
// reset the scroll bar
LCSetMax( bar, max );
LCSetValue( bar, value );
// if value exceeds max then the bottom of the destRect is above
// the bottom of the view rectangle: we need to scroll the text downward
if ( value > max )
ScrollBarChanged();
// now do the horizontal axis
// get scroll bar handle
bar = fDocHScroll;
// calculate new scroll bar settings
// NOTE: (destRect.bottom - destRect.top) always equals the total text height because
// WASTE automatically updates destRect.bottom whenever line breaks are recalculated
value = viewRect.left - destRect.left;
max = value + (destRect.right - viewRect.right);
// make sure max is always non-negative
if ( max <= 0 )
max = 0;
// reset the scroll bar
LCSetMax( bar, max );
LCSetValue( bar, value );
// if value exceeds max then the bottom of the destRect is above
// the bottom of the view rectangle: we need to scroll the text downward
if ( value > max )
ScrollBarChanged();
SetPort( savePort );
}
/**********************************************************************************/
void TTextWindow::ScrollBarChanged( void )
{
// scroll text to reflect new scroll bar setting
LongRect viewRect, destRect;
WEGetViewRect( &viewRect, fWEHandle );
WEGetDestRect( &destRect, fWEHandle );
WEScroll( viewRect.left - destRect.left - LCGetValue(fDocHScroll),
viewRect.top - destRect.top - LCGetValue(fDocVScroll), fWEHandle );
}
/**********************************************************************************/
void TTextWindow::GetWindowSizeLimits(Rect *limits)
{
limits->top = kMinWindowHeight + fToolbarHeight;
limits->left = kMinWindowWidth;
limits->right = kMaxWindowWidth;
limits->bottom = gDeskRectangle.bottom - gDeskRectangle.top;
}
/**********************************************************************************/
void TTextWindow::GetPerfectWindowSize(Rect * perfectSize)
{
perfectSize->top = 0;
perfectSize->left = 0;
perfectSize->right = kMaxWindowWidth;
perfectSize->bottom = gDeskRectangle.bottom - gDeskRectangle.top;
}
/**********************************************************************************/
Boolean TTextWindow::CanClose(Boolean quitting)
{
SetCursor(&qd.arrow); // in case the user hit cmd-W while over the text area
if (WEGetModCount(fWEHandle) > 0) // > 0 means that doc is dirty
{
Str255 s1, s3;
short reply;
GetIndString(s1, 200, 1);
GetIndString(s3, kStandardCloseStrings, quitting ? kQuittingStr : kClosingStr);
ParamText(s1, this->fName, s3, "\p");
reply = StandardAlert(kStandardCloseAlertID, 1, 2);
switch (reply)
{
case 3: // Don't Save
return true;
break;
case 2: // Cancel
return false;
break;
case 1: // OK (i.e. save the document)
GetIndString(s1, 200, 3);
ParamText(s1, "\p", "\p", "\p");
this->DoCommand(cSave);
break;
}
}
return true;
}
/**********************************************************************************/
void TTextWindow::Click(EventRecord * anEvent)
{
long command;
ControlRef theControl;
Point hitPt = anEvent->where;
this->Select(); // so we don't have to call the inherited class
SetPortWindowPort(fWindow);
GlobalToLocal(&hitPt);
FindControl(hitPt, fWindow, &theControl);
if (theControl)
{
command = GetControlReference(theControl);
if (command != 0)
{
if ( TrackControl(theControl, anEvent->where, nil) )
QueueCommand(command);
}
}
}
/**********************************************************************************/
Boolean TTextWindow::EventFilter(EventRecord* theEvent)
{
Rect textRect;
GrafPtr savePort;
Boolean result = false; // false means click should not activate this window
Point hitPt;
if ( theEvent->what != mouseDown )
{
return TWindow::EventFilter(theEvent);
}
hitPt = theEvent->where; // get the click position
if (!IsPointInContentRgn(hitPt))
{
return TWindow::EventFilter(theEvent);
}
// set the port to our window's port
GetPort( &savePort );
SetPortWindowPort(fWindow);
GlobalToLocal( &hitPt );
// filter out clicks in the toolbar
textRect = GetContentsBounds();
GlobalToLocal((Point*) &textRect.top);
GlobalToLocal((Point*) &textRect.bottom);
textRect.bottom = textRect.top + fToolbarHeight;
if (PtInRect(hitPt, &textRect)) // this is the toolbar, I'm just using textrect for convenience
{
this->Click(theEvent);
return true;
}
// convert the point to local coordinates
GetGrowIconRect( &textRect );
if (PtInRect(hitPt, &textRect)) // this is the growrect, I'm just using textrect for convenience
{
return TWindow::EventFilter(theEvent);
}
// a click in an inactive window should normally activate it,
// but the availability of the Drag Manager introduces an exception to this rule:
// a click in the background selection may start a drag gesture,
// without activating the window
GetTextRect( &textRect );
if ( PtInRect( hitPt, &textRect ) )
WEClick( hitPt, theEvent->modifiers, theEvent->when, fWEHandle );
else
DoScrollBar( hitPt, theEvent->modifiers );
// restore the port
SetPort( savePort );
return true;
}
/**********************************************************************************/
void TTextWindow::KeyDown(EventRecord* theEvent)
{
SignedByte keyCode;
char key;
keyCode = ( theEvent->message & keyCodeMask ) >> 8;
key = (char) (theEvent->message & charCodeMask);
switch ( keyCode )
{
case keyPgUp:
case keyPgDn:
case keyHome:
case keyEnd:
DoScrollKey( keyCode ); // page movement keys are handled by DoScrollKey()
break;
default:
WEKey( key, theEvent->modifiers, fWEHandle );
break;
}
return;
}
/**********************************************************************************/
void TTextWindow::Activate(Boolean activating)
{
GrafPtr savePort;
ControlPartCode barHilite;
TWindow::Activate(activating);
if (!fWEHandle)
return; // caveat coder: this routine can be called before fWEHandle is even created
GetPort( &savePort );
SetPortWindowPort( fWindow );
// activate or deactivate the text (and any other relevant stuff) depending on just
// what we're doing here...
if ( activating )
{
WEActivate( fWEHandle );
barHilite = kControlNoPart;
InsertMenu( fFontMenu, 0 );
InsertMenu( fSizeMenu, 0 );
InsertMenu( fStyleMenu, 0 );
InsertMenu( fColorMenu, 0 );
}
else
{
WEDeactivate( fWEHandle );
barHilite = kControlDisabledPart;
DeleteMenu( mFont );
DeleteMenu( mSize );
DeleteMenu( mStyle );
DeleteMenu( mColor );
}
gMenuBar->Invalidate();
// redraw the scroll bars with the new highlighting (and validate their rects)
HiliteControl( fDocVScroll, barHilite );
HiliteControl( fDocHScroll, barHilite );
DrawJustTheGrowIcon(fWindow); // This does the "right thing" whether hiliting or unhiliting
SetPort(savePort);
}
/**********************************************************************************/
void TTextWindow::Idle(EventRecord* /*anEvent*/)
{
WEStyleMode mode;
WEAlignment alignment;
TextStyle ts;
Boolean temp;
WEIdle(nil, fWEHandle);
if (fSaveButton)
HiliteControl( fSaveButton, WEGetModCount(fWEHandle) ? 0 : 255 );
mode = weDoFace + weDoColor; // query about all attributes
temp = WEContinuousStyle( &mode, &ts, fWEHandle );
if ( mode & weDoFace )
{
HiliteControl( fPlainButton, ( ts.tsFace == 0 ) ? kControlButtonPart : 0 );
HiliteControl( fBoldButton, ( ts.tsFace & bold ) ? kControlButtonPart : 0 );
HiliteControl( fItalicButton, ( ts.tsFace & italic ) ? kControlButtonPart : 0 );
HiliteControl( fUnderlineButton, ( ts.tsFace & underline ) ? kControlButtonPart : 0 );
}
else
{
HiliteControl( fPlainButton, 0 );
HiliteControl( fBoldButton, 0 );
HiliteControl( fItalicButton, 0 );
HiliteControl( fUnderlineButton, 0 );
}
alignment = WEGetAlignment( fWEHandle );
HiliteControl( fLeftAlignButton, ( alignment == weFlushLeft ) ? kControlButtonPart : 0 );
HiliteControl( fCenterAlignButton, ( alignment == weCenter ) ? kControlButtonPart : 0 );
HiliteControl( fRightAlignButton, ( alignment == weFlushRight ) ? kControlButtonPart : 0 );
HiliteControl( fJustifyButton, ( alignment == weJustify ) ? kControlButtonPart : 0 );
if ( mode & weDoColor )
{
CommandID color = SelectedColor(ts.tsColor);
HiliteControl( fBlackButton, ( color == cBlack ) ? kControlButtonPart : 0 );
HiliteControl( fBlueButton, ( color == cBlue ) ? kControlButtonPart : 0 );
HiliteControl( fGreenButton, ( color == cGreen ) ? kControlButtonPart : 0 );
HiliteControl( fRedButton, ( color == cRed ) ? kControlButtonPart : 0 );
HiliteControl( fPinkButton, ( color == cPink ) ? kControlButtonPart : 0 );
HiliteControl( fOrangeButton, ( color == cOrange ) ? kControlButtonPart : 0 );
HiliteControl( fBrownButton, ( color == cBrown ) ? kControlButtonPart : 0 );
}
else
{
HiliteControl( fBlackButton, 0 );
HiliteControl( fBlueButton, 0 );
HiliteControl( fGreenButton, 0 );
HiliteControl( fRedButton, 0 );
HiliteControl( fPinkButton, 0 );
HiliteControl( fOrangeButton, 0 );
HiliteControl( fBrownButton, 0 );
}
}
/**********************************************************************************/
void TTextWindow::Draw(void)
{
Rect bounds = GetContentsBounds();
Rect toolbarBounds;
RgnHandle boundsRgn = NewRgn();
TWindow::Draw();
GlobalToLocal((Point*) &bounds.top);
GlobalToLocal((Point*) &bounds.bottom);
if (fToolbarHeight > 0)
{
RGBColor myBackColor = { kAppleGrayscale2, kAppleGrayscale2, kAppleGrayscale2 };
toolbarBounds = bounds;
toolbarBounds.bottom = toolbarBounds.top + fToolbarHeight;
// Draw Toolbar Background
RGBBackColor(&myBackColor);
EraseRect(&toolbarBounds);
MoveTo(0, fToolbarHeight - 1);
LineTo(toolbarBounds.right, fToolbarHeight - 1);
MoveTo(0, fToolbarHeight - 3);
LineTo(toolbarBounds.right, fToolbarHeight - 3);
Draw1Control(fSaveButton);
TextSize(9);
TextFont(applFont);
TextFace(condense);
MoveTo(122, 18);
DrawString("\pStyle:");
Draw1Control(fPlainButton);
Draw1Control(fBoldButton);
Draw1Control(fItalicButton);
Draw1Control(fUnderlineButton);
MoveTo(105, 34);
DrawString("\pAlignment:");
Draw1Control(fLeftAlignButton);
Draw1Control(fCenterAlignButton);
Draw1Control(fRightAlignButton);
Draw1Control(fJustifyButton);
MoveTo(122, 50);
DrawString("\pColor:");
Draw1Control(fBlackButton);
Draw1Control(fBlueButton);
Draw1Control(fGreenButton);
Draw1Control(fRedButton);
Draw1Control(fPinkButton);
Draw1Control(fOrangeButton);
Draw1Control(fBrownButton);
BackColor(whiteColor);
}
bounds.right -= kScrollbarAdjust;
bounds.bottom -= kScrollbarAdjust;
bounds.top += fToolbarHeight;
EraseRect(&bounds);
RectRgn(boundsRgn, &bounds);
WEUpdate(boundsRgn, fWEHandle);
GetWindowContentRgn(fWindow, boundsRgn); // recycle boundsRgn
UpdateControls(fWindow, boundsRgn);
DisposeRgn(boundsRgn);
DrawJustTheGrowIcon(fWindow);
}
/**********************************************************************************/
void TTextWindow::AdjustCursor(EventRecord* anEvent)
{
Rect textBox;
Point where = anEvent->where;
SetPortWindowPort(fWindow);
GetTextRect(&textBox);
GlobalToLocal(&where);
if ( PtInRect(where, &textBox) )
{
SetCursor(*GetCursor(iBeamCursor));
}
else
{
SetCursor(&qd.arrow);
}
}
/**********************************************************************************/
void TTextWindow::SetUpStaticMenu( void )
{
// We only need 1 copy!
if (!TTextWindow::fFontMenu)
{
TTextWindow::fFontMenu = GetMenu( mFont );
if (TTextWindow::fFontMenu)
{
AppendResMenu(TTextWindow::fFontMenu, 'FONT');
InsertMenu(TTextWindow::fFontMenu, 0);
}
}
if (!TTextWindow::fSizeMenu)
{
TTextWindow::fSizeMenu = gMenuBar->GetMenuFromCMNU( mSize );
}
if (!TTextWindow::fStyleMenu)
{
TTextWindow::fStyleMenu = gMenuBar->GetMenuFromCMNU( mStyle );
}
if (!TTextWindow::fColorMenu)
{
TTextWindow::fColorMenu = gMenuBar->GetMenuFromCMNU( mColor );
}
}
/**********************************************************************************/
void TTextWindow::AdjustMenusBeforeMenuSelection(void)
{
WEStyleMode mode;
WEAlignment alignment;
WEActionKind actionKind;
TextStyle ts;
Boolean temp;
gMenuBar->EnableCommand ( cSave, WEGetModCount(fWEHandle) );
gMenuBar->EnableCommand ( cSaveAs, true );
gMenuBar->EnableCommand ( cClose, true );
actionKind = WEGetUndoInfo( &temp, fWEHandle );
gMenuBar->EnableCommand ( cUndo, ( actionKind != weAKNone ) );
gMenuBar->EnableCommand ( cCut, HaveSelection() );
gMenuBar->EnableCommand ( cCopy, HaveSelection() );
gMenuBar->EnableCommand ( cPaste, WECanPaste( fWEHandle ) );
gMenuBar->EnableCommand ( cClear, HaveSelection() );
gMenuBar->EnableCommand ( cSelectAll, ( WEGetTextLength( fWEHandle ) != 0 ));
mode = weDoAll; // query about all attributes
temp = WEContinuousStyle( &mode, &ts, fWEHandle );
if ( mode & weDoFont )
{
WEStyleMode mode;
TextStyle ts;
short item;
Str255 itemText;
MenuRef menu = GetMenuHandle( mFont );
mode = weDoFont; // query about all attributes
WEContinuousStyle( &mode, &ts, fWEHandle );
// first, remove all check marks
for ( item = CountMItems( menu ); item >= 1; item-- )
CheckItem( menu, item, false );
// if there is a continuous font all over the selection range, check the
// corresponding menu item
if ( ( mode & weDoFont ) != 0 )
{
GetFontName( ts.tsFont, itemText );
CheckItem( menu, FindMenuItemText( menu, itemText ), true );
}
}
if ( mode & weDoSize )
{
gMenuBar->EnableAndCheckCommand( c9Point, true, ( ts.tsSize == 9 ) );
gMenuBar->EnableAndCheckCommand( c10Point, true, ( ts.tsSize == 10 ) );
gMenuBar->EnableAndCheckCommand( c12Point, true, ( ts.tsSize == 12 ) );
gMenuBar->EnableAndCheckCommand( c14Point, true, ( ts.tsSize == 14 ) );
gMenuBar->EnableAndCheckCommand( c18Point, true, ( ts.tsSize == 18 ) );
gMenuBar->EnableAndCheckCommand( c24Point, true, ( ts.tsSize == 24 ) );
gMenuBar->EnableAndCheckCommand( c36Point, true, ( ts.tsSize == 36 ) );
gMenuBar->EnableAndCheckCommand( c48Point, true, ( ts.tsSize == 48 ) );
gMenuBar->EnableAndCheckCommand( c72Point, true, ( ts.tsSize == 72 ) );
switch (ts.tsSize)
{
case 9:
case 10:
case 12:
case 14:
case 18:
case 24:
case 36:
case 48:
case 72:
gMenuBar->EnableAndCheckCommand( cOtherSize, true, false );
break;
default:
gMenuBar->EnableAndCheckCommand( cOtherSize, true, true );
break;
}
gMenuBar->SetItemStyle(c9Point, RealFont(ts.tsFont, 9) ? outline : 0);
gMenuBar->SetItemStyle(c10Point, RealFont(ts.tsFont, 10) ? outline : 0);
gMenuBar->SetItemStyle(c12Point, RealFont(ts.tsFont, 12) ? outline : 0);
gMenuBar->SetItemStyle(c14Point, RealFont(ts.tsFont, 14) ? outline : 0);
gMenuBar->SetItemStyle(c18Point, RealFont(ts.tsFont, 18) ? outline : 0);
gMenuBar->SetItemStyle(c24Point, RealFont(ts.tsFont, 24) ? outline : 0);
gMenuBar->SetItemStyle(c36Point, RealFont(ts.tsFont, 36) ? outline : 0);
gMenuBar->SetItemStyle(c48Point, RealFont(ts.tsFont, 48) ? outline : 0);
gMenuBar->SetItemStyle(c72Point, RealFont(ts.tsFont, 72) ? outline : 0);
}
if ( mode & weDoFace )
{
gMenuBar->EnableAndCheckCommand( cPlainText, true, ( ts.tsFace == 0 ) );
gMenuBar->EnableAndCheckCommand( cBold, true, ( ts.tsFace & bold ) );
gMenuBar->EnableAndCheckCommand( cItalic, true, ( ts.tsFace & italic ) );
gMenuBar->EnableAndCheckCommand( cUnderline, true, ( ts.tsFace & underline ) );
gMenuBar->EnableAndCheckCommand( cOutline, true, ( ts.tsFace & outline ) );
gMenuBar->EnableAndCheckCommand( cShadow, true, ( ts.tsFace & shadow ) );
gMenuBar->EnableAndCheckCommand( cCondensed, true, ( ts.tsFace & condense ) );
gMenuBar->EnableAndCheckCommand( cExtended, true, ( ts.tsFace & extend ) );
}
if ( mode & weDoColor )
{
CommandID color = SelectedColor(ts.tsColor);
gMenuBar->EnableAndCheckCommand( cBlack, true, color == cBlack );
gMenuBar->EnableAndCheckCommand( cBlue, true, color == cBlue );
gMenuBar->EnableAndCheckCommand( cGreen, true, color == cGreen );
gMenuBar->EnableAndCheckCommand( cRed, true, color == cRed );
gMenuBar->EnableAndCheckCommand( cPink, true, color == cPink );
gMenuBar->EnableAndCheckCommand( cOrange, true, color == cOrange );
gMenuBar->EnableAndCheckCommand( cBrown, true, color == cBrown );
gMenuBar->EnableAndCheckCommand( cOtherColor, true, color == cOtherColor );
}
else
{
gMenuBar->EnableAndCheckCommand( cBlack, true, false );
gMenuBar->EnableAndCheckCommand( cBlue, true, false );
gMenuBar->EnableAndCheckCommand( cGreen, true, false );
gMenuBar->EnableAndCheckCommand( cRed, true, false );
gMenuBar->EnableAndCheckCommand( cPink, true, false );
gMenuBar->EnableAndCheckCommand( cOrange, true, false );
gMenuBar->EnableAndCheckCommand( cBrown, true, false );
gMenuBar->EnableAndCheckCommand( cOtherColor, true, false );
}
alignment = WEGetAlignment( fWEHandle );
gMenuBar->EnableAndCheckCommand( cAlignDefault, true, ( alignment == weFlushDefault ) );
gMenuBar->EnableAndCheckCommand( cAlignLeft, true, ( alignment == weFlushLeft ) );
gMenuBar->EnableAndCheckCommand( cAlignCenter, true, ( alignment == weCenter ) );
gMenuBar->EnableAndCheckCommand( cAlignRight, true, ( alignment == weFlushRight ) );
gMenuBar->EnableAndCheckCommand( cAlignJustify, true, ( alignment == weJustify ) );
}
/**********************************************************************************/
// Return a rectangle that is inset from the portRect by the size of
// the scrollbars and a little extra margin.
void TTextWindow::GetWERect(LongRect& teRect)
{
Rect tempRect;
tempRect = GetContentsBounds();
LocalToGlobal((Point*) &tempRect.top);
LocalToGlobal((Point*) &tempRect.bottom);
InsetRect(&tempRect, kTextMargin, kTextMargin); // adjust for margin
tempRect.bottom = tempRect.bottom - kScrollbarAdjust; // and for the scrollbars
tempRect.right = tempRect.right - kScrollbarAdjust;
tempRect.top += fToolbarHeight; // possibly make room for a toolbar
WERectToLongRect(&tempRect, &teRect);
}
/**********************************************************************************/
void TTextWindow::GetGrowIconRect( Rect *iconRect )
{
Rect portRect = GetWindowPort(fWindow)->portRect;
iconRect->top = portRect.bottom - (kScrollbarWidth - 2);
iconRect->left = portRect.right - (kScrollbarWidth - 2);
iconRect->bottom = portRect.bottom;
iconRect->right = portRect.right;
}
/**********************************************************************************/
void TTextWindow::GetTextRect( Rect *textRect )
{
Rect portRect = GetWindowPort(fWindow)->portRect;
textRect->top = fToolbarHeight;
textRect->left = 0;
textRect->bottom = portRect.bottom - (kScrollbarWidth - 1);
textRect->right = portRect.right - (kScrollbarWidth - 1);
InsetRect( textRect, kTextMargin, kTextMargin );
}
/**********************************************************************************/
void TTextWindow::GetScrollBarRect( VHSelect axis, Rect *barRect )
{
Rect portRect = GetWindowPort(fWindow)->portRect;
switch ( axis )
{
case v:
barRect->top = -1 + fToolbarHeight;
barRect->left = portRect.right - (kScrollbarWidth - 1);
barRect->bottom = portRect.bottom - (kScrollbarWidth - 2);
barRect->right = portRect.right + 1;
break;
case h:
barRect->top = portRect.bottom - (kScrollbarWidth - 1);
barRect->left = -1;
barRect->bottom = portRect.bottom + 1;
barRect->right = portRect.right - (kScrollbarWidth - 2 );
break;
default:
break;
}
}
/**********************************************************************************/
// Return boolean value indicating that there is or is not a
// selection in the document
Boolean TTextWindow::HaveSelection()
{
long start, end;
WEGetSelection(&start, &end, fWEHandle);
if ( start < end )
return true;
else
return false;
}
/**********************************************************************************/
Boolean TTextWindow::DoMenuSelection(short menu, short item)
{
if (menu == mFont)
{
Str255 fontName;
TextStyle ts;
GetMenuItemText( GetMenuHandle( mFont ), item, fontName );
GetFNum( fontName, &ts.tsFont );
if ( WESetStyle( weDoFont, &ts, fWEHandle ) != noErr )
return true;
}
return TWindow::DoMenuSelection(menu, item);
}
/**********************************************************************************/
Boolean TTextWindow::DoCommand(CommandID theCommand)
{
WEStyleMode mode = weDoSize;
TextStyle ts;
Boolean restyle = false;
switch(theCommand)
{
case cUndo:
if ( WEUndo(fWEHandle) != noErr )
return true;
break;
case cCut:
if ( WECut(fWEHandle) != noErr )
return true;
break;
case cCopy:
if ( WECopy(fWEHandle) != noErr )
return true;
break;
case cPaste:
if ( WEPaste(fWEHandle) != noErr )
return true;
break;
case cSelectAll:
WESetSelection( 0, LONG_MAX, fWEHandle );
return true;
break;
case cClear:
if ( WEDelete(fWEHandle) != noErr )
return true;
break;
case c9Point:
ts.tsSize = 9;
restyle = true;
break;
case c10Point:
ts.tsSize = 10;
restyle = true;
break;
case c12Point:
ts.tsSize = 12;
restyle = true;
break;
case c14Point:
ts.tsSize = 14;
restyle = true;
break;
case c18Point:
ts.tsSize = 18;
restyle = true;
break;
case c24Point:
ts.tsSize = 24;
restyle = true;
break;
case c36Point:
ts.tsSize = 36;
restyle = true;
break;
case c48Point:
ts.tsSize = 48;
restyle = true;
break;
case c72Point:
ts.tsSize = 72;
restyle = true;
break;
case cOtherSize:
{
DialogRef theDialog = GetNewDialog(260, nil, (WindowRef)-1);
if (theDialog)
{
short itemHit = 0;
Str255 theSize;
long pointSize;
ShowWindow(GetDialogWindow(theDialog));
while (itemHit != 1 && itemHit != 2)
{
ModalDialog(nil, &itemHit);
}
GetEditText(theDialog, 3, theSize);
StringToNum(theSize, &pointSize);
HideWindow(GetDialogWindow(theDialog));
DisposeDialog(theDialog);
if (itemHit == 1 && pointSize > 3 && pointSize < 500 )
{
ts.tsSize = pointSize;
restyle = true;
}
}
}
break;
case cOtherColor:
{
Point where = {-1, -1};
unsigned short localMode = doColor;
if (!WEContinuousStyle(&localMode, &ts, fWEHandle))
{
ts.tsColor.red = 0xFFFF;
ts.tsColor.green = 0xFFFF;
ts.tsColor.blue = 0xFFFF;
}
GetColor(where, "\pPick a text color:", &ts.tsColor, &ts.tsColor);
restyle = true;
mode = weDoColor;
}
break;
case cBlack:
ts.tsColor.red = 0x0000;
ts.tsColor.green = 0x0000;
ts.tsColor.blue = 0x0000;
restyle = true;
mode = weDoColor;
break;
case cBrown:
ts.tsColor.red = 42253;
ts.tsColor.green = 30947;
ts.tsColor.blue = 0;
restyle = true;
mode = weDoColor;
break;
case cBlue:
ts.tsColor.red = 0;
ts.tsColor.green = 0;
ts.tsColor.blue = 54272;
restyle = true;
mode = weDoColor;
break;
case cGreen:
ts.tsColor.red = 0;
ts.tsColor.green = 32768;
ts.tsColor.blue = 4528;
restyle = true;
mode = weDoColor;
break;
case cRed:
ts.tsColor.red = 56683;
ts.tsColor.green = 2242;
ts.tsColor.blue = 1698;
restyle = true;
mode = weDoColor;
break;
case cPink:
ts.tsColor.red = 65535;
ts.tsColor.green = 0;
ts.tsColor.blue = 52428;
restyle = true;
mode = weDoColor;
break;
case cOrange:
ts.tsColor.red = 65535;
ts.tsColor.green = 27594;
ts.tsColor.blue = 0;
restyle = true;
mode = weDoColor;
break;
case cPlainText:
mode = weDoFace + weDoToggleFace;
ts.tsFace = 0;
restyle = true;
break;
case cBold:
ts.tsFace = bold;
mode = weDoFace + weDoToggleFace;
restyle = true;
break;
case cItalic:
ts.tsFace = italic;
mode = weDoFace + weDoToggleFace;
restyle = true;
break;
case cUnderline:
ts.tsFace = underline;
mode = weDoFace + weDoToggleFace;
restyle = true;
break;
case cOutline:
ts.tsFace = outline;
mode = weDoFace + weDoToggleFace;
restyle = true;
break;
case cShadow:
ts.tsFace = shadow;
mode = weDoFace + weDoToggleFace;
restyle = true;
break;
case cCondensed:
ts.tsFace = condense;
mode = weDoFace + weDoToggleFace;
restyle = true;
break;
case cExtended:
ts.tsFace = extend;
mode = weDoFace + weDoToggleFace;
restyle = true;
break;
case cAlignLeft:
WESetAlignment( weFlushLeft, fWEHandle );
return true;
break;
case cAlignCenter:
WESetAlignment( weCenter, fWEHandle );
return true;
break;
case cAlignRight:
WESetAlignment( weFlushRight, fWEHandle );
return true;
break;
case cAlignDefault:
WESetAlignment( weFlushDefault, fWEHandle );
return true;
break;
case cAlignJustify:
WESetAlignment( weJustify, fWEHandle );
return true;
break;
case cSave:
{
FSSpec filespec;
fBackingFile->GetSpecifier(filespec);
if (filespec.name[0] == 0)
return this->DoCommand(cSaveAs);
else
{
OSErr err;
err = SafeSaveFile(true, filespec);
// display an alert box if anything went wrong
if (err != noErr)
{
Str255 errorNum;
NumToString(err, errorNum);
ParamText("\pError #", errorNum, "\p encountered while trying to save the file.", "\p");
StandardAlert( 128 );
}
else
{
pcpy(fName, filespec.name);
SetWTitle(fWindow, fName);
}
}
return true;
}
break;
case cSaveAs:
{
StandardFileReply reply;
OSErr err;
StScrpHandle hStyles = NULL;
WESoupHandle hSoup = NULL;
StandardPutFile("\pSave this document as:", this->fName, &reply);
if (!reply.sfGood)
return true;
err = SafeSaveFile(reply.sfReplacing, reply.sfFile);
if (err != noErr)
{
Str255 errorNum;
NumToString(err, errorNum);
ParamText("\pError #", errorNum, "\p encountered while trying to save the file.", "\p");
StandardAlert( 128 );
}
else
{
pcpy(fName, reply.sfFile.name);
SetWTitle(fWindow, fName);
}
return true;
}
break;
}
if (restyle)
{
if ( WESetStyle( mode, &ts, fWEHandle ) != noErr )
return true;
}
return TWindow::DoCommand(theCommand);
}
/**********************************************************************************/
static pascal void TextScrolled( WEReference we )
{
TTextWindow* This;
// retrieve the TTextWindow pointer stored in the WE instance as a "reference constant"
if (WEGetInfo(weRefCon, &This, we) != noErr )
return;
// make sure the scroll bars are in synch with the destination rectangle
This->AdjustBars();
return;
}
/**********************************************************************************/
void TTextWindow::DoScrollKey( SignedByte keyCode )
{
ControlRef bar;
long v;
LongRect viewRect;
bar = fDocVScroll;
// get current scroll bar value
v = LCGetValue( bar );
// get text view rect
WEGetViewRect( &viewRect, fWEHandle );
switch ( keyCode )
{
case keyPgUp:
v -= ( viewRect.bottom - viewRect.top ) + kScrollDelta;
break;
case keyPgDn:
v += ( viewRect.bottom - viewRect.top ) - kScrollDelta;
break;
case keyHome:
v = 0;
break;
case keyEnd:
v = LONG_MAX;
break;
default:
break;
} // end switch keyCode
// set the new scroll bar value and scroll the text pane accordingly
LCSetValue( bar, v );
ScrollBarChanged();
}
/**********************************************************************************/
void TTextWindow::DoScrollBar( Point hitPt, EventModifiers modifiers )
{
ControlRef bar;
LongRect viewRect;
ControlPartCode partCode;
short step;
WEGetViewRect(&viewRect, fWEHandle);
// find out which scrollbar was hit (if any) and in which part
partCode = FindControl( hitPt, fWindow, &bar );
if ( bar != NULL )
{
// dispatch on partCode
if ( partCode == kControlIndicatorPart )
{
// click in thumb: call TrackControl with no actionProc and adjust text
partCode = TrackControl( bar, hitPt, NULL );
LCSynch( bar );
this->ScrollBarChanged();
} // end if partCode == kControlIndicatorPart
else
{
if ( bar == fDocVScroll )
{
// dispatch our partCode
switch( partCode )
{
case kControlUpButtonPart:
if ( (modifiers & optionKey ) == 0 )
step = -kScrollDelta;
else
step = -1;
break;
case kControlDownButtonPart:
if ( (modifiers & optionKey ) == 0 )
step = +kScrollDelta;
else
step = 1;
break;
case kControlPageUpPart:
step = -( viewRect.bottom - viewRect.top) + kScrollDelta;
break;
case kControlPageDownPart:
step = ( viewRect.bottom - viewRect.top ) - kScrollDelta;
break;
default:
step = 0;
break;
}
}
else if ( bar == fDocHScroll )
{
// dispatch our partCode
switch( partCode )
{
case kControlUpButtonPart:
if ( (modifiers & optionKey ) == 0 )
step = -kScrollDelta;
else
step = -1;
break;
case kControlDownButtonPart:
if ( (modifiers & optionKey ) == 0 )
step = +kScrollDelta;
else
step = 1;
break;
case kControlPageUpPart:
step = -( viewRect.right - viewRect.left) + kScrollDelta;
break;
case kControlPageDownPart:
step = ( viewRect.right - viewRect.left ) - kScrollDelta;
break;
default:
step = 0;
break;
}
}
// save step in a static variable for our ScrollProc callback
sScrollStep = step;
// track the mouse
if ( sScrollProc == NULL )
sScrollProc = NewControlActionProc( ScrollProc );
partCode = TrackControl( bar, hitPt, sScrollProc );
}
}
}
/**********************************************************************************/
// this is a callback routine called by the Toolbox Control Manager
// move the scroll bar thumb and scroll the text accordingly
static pascal void ScrollProc( ControlRef bar, ControlPartCode partCode )
{
long value, step;
if ( partCode == kControlNoPart )
return;
value = LCGetValue( bar );
step = sScrollStep;
if ( (( value < LCGetMax( bar )) && ( step > 0 )) || (( value > 0 ) && ( step < 0 ) ) )
{
LCSetValue( bar, value + step );
TTextWindow* This = (TTextWindow*)GetWRefCon(FrontWindow());
This->ScrollBarChanged();
}
}
OSErr TTextWindow::DragEnterWindow(DragReference theDrag)
{
return WETrackDrag( dragTrackingEnterWindow, theDrag, fWEHandle );
}
OSErr TTextWindow::DragInWindow(DragReference theDrag)
{
return WETrackDrag( dragTrackingInWindow, theDrag, fWEHandle );
}
OSErr TTextWindow::DragLeaveWindow(DragReference theDrag)
{
return WETrackDrag( dragTrackingLeaveWindow, theDrag, fWEHandle );
}
OSErr TTextWindow::HandleDrop(DragReference theDrag)
{
return WEReceiveDrag( theDrag, fWEHandle );
}
/**********************************************************************************/
pascal OSErr TranslateDrag( DragReference theDrag, ItemReference theItem, FlavorType requestedType, Handle dataHandle )
{
#pragma unused ( theDrag, theItem, dataHandle )
if ( ( requestedType != kTypeText ) && ( requestedType != kTypeTextReadOnly ) )
return badDragFlavorErr;
return noErr;
}
/**********************************************************************************/
static short FindMenuItemText( MenuRef menu, ConstStr255Param stringToFind )
{
short item;
Str255 itemString;
for ( item = CountMItems( menu ); item >= 1; item-- )
{
GetMenuItemText( menu, item, itemString );
if ( EqualString( itemString, stringToFind, false, false ) )
break;
}
return item;
}
/**********************************************************************************/
OSErr TTextWindow::SafeSaveFile(Boolean replacing, FSSpec& filespec)
{
FInfo fileInfo;
Size textSize;
OSStatus err = noErr;
OSErr tempErr = noErr;
short dataForkRefNum = 0;
short resForkRefNum = 0;
Handle hText = NULL;
StScrpHandle hStyles = NULL;
WESoupHandle hSoup = NULL;
unsigned long theTime;
Str255 tempFileName;
FSSpec tempFileSpec;
short tempVRef; // volume reference # for the temp file
long tempDirID; // directory ID of the temp file
TFile* tempFile;
// will we be replacing an existing file?
if ( replacing )
{
err = CheckObjectLock(filespec.vRefNum, filespec.parID, (StringPtr) filespec.name);
if ( err != noErr ) // if it returns noErr, the file/directory is NOT locked
return err;
// create the temporary file name. the name doesn't have to make sense, just
// be unique
GetDateTime( &theTime );
NumToString( theTime, tempFileName );
// find the temporary items folder on the file's volume; create it if necessary
// it is important that the temp folder (and the temp file) and the "original" target
// file be on the same volume; if not, FSpExchangeFiles will return diffVolErr (-1303)
// and won't work
err = FindFolder( filespec.vRefNum, kTemporaryFolderType, kCreateFolder, &tempVRef, &tempDirID );
if ( err != noErr )
return err; // for now, do nothing, just return
// make an FSSpec for the temp file
err = FSMakeFSSpec( tempVRef, tempDirID, tempFileName, &tempFileSpec );
if ( (err != noErr) && (err != fnfErr ) )
return err;
tempFile = new TFile(tempFileSpec);
}
else
fBackingFile->SetSpecifier(filespec);
// create a new file. if we're replacing, make a temp file. if it's a
// new file from the onset, just create the file
// should we allow people to choose the file to create? i.e. create a TEXT
// file or a ttro file? if you'd like to do this, you'll want to find this
// info out when calling CustomPutFile() and pass that info to this function
if ( replacing )
err = tempFile->CreateNewFile('trsh', 'trsh', smSystemScript );
else
err = fBackingFile->CreateNewFile('75SL', kTypeText, smSystemScript );
err = ResError();
if ( err != noErr )
goto cleanup;
// if replacing an old file, copy the old file information
if ( replacing )
{
err = FSpSetFInfo( &tempFileSpec, &fileInfo );
if ( err != noErr )
{
goto cleanup;
}
}
// open the data fork for writing
if ( replacing )
dataForkRefNum = tempFile->OpenDataFork(fsWrPerm);
else
dataForkRefNum = fBackingFile->OpenDataFork(fsWrPerm);
if ( err != noErr )
goto cleanup;
// set the end-of-file
err = SetEOF( dataForkRefNum, 0 );
if ( err != noErr )
goto cleanup;
// get the text handle from the WE instance
// WEGetText returns the original handle, not a copy, so don't dispose of it!!
hText = WEGetText( fWEHandle );
textSize = GetHandleSize( hText );
// write the text
HLock( hText );
if (replacing)
textSize = tempFile->WriteDataFork(*hText, textSize);
else
textSize = fBackingFile->WriteDataFork(*hText, textSize);
HUnlock( hText );
if ( textSize == 0 )
{
err = eofErr;
goto cleanup;
}
if (replacing)
err = tempFile->CloseDataFork();
else
err = fBackingFile->CloseDataFork();
// open the resource file for writing
if ( replacing )
resForkRefNum = tempFile->OpenResourceFork( fsWrPerm );
else
resForkRefNum = fBackingFile->OpenResourceFork( fsWrPerm );
err = ResError();
if ( err != noErr )
goto cleanup;
// allocate temporary handles to hold the style scrap and the soup
// try tapping temporary memory since WECopyRange() could get huge
hStyles = (StScrpHandle)TempNewHandle( 0, &tempErr );
if ( tempErr != noErr )
{
err = tempErr;
goto cleanup;
}
hSoup = (WESoupHandle)TempNewHandle( 0, &tempErr );
if ( tempErr != noErr )
{
err = tempErr;
goto cleanup;
}
// create the style scrap and the soup
err = WECopyRange( 0, LONG_MAX, NULL, hStyles, hSoup, fWEHandle );
if ( err != noErr )
goto cleanup;
// make them resource handles
err = RMAddResourceCompat( (Handle)hStyles, kTypeStyles, 128, "\p" );
if ( err != noErr )
goto cleanup;
err = RMAddResourceCompat( (Handle)hSoup, kTypeSoup, 128, "\p" );
if ( err != noErr )
goto cleanup;
// write them to the resource file
err = RMChangedResourceCompat( (Handle)hStyles );
if ( err != noErr )
goto cleanup;
err = RMWriteResourceCompat( (Handle)hStyles );
if ( err != noErr )
goto cleanup;
err = RMChangedResourceCompat( (Handle)hSoup );
if ( err != noErr )
goto cleanup;
err = RMWriteResourceCompat( (Handle)hSoup );
if ( err != noErr )
goto cleanup;
if ( replacing )
err = tempFile->CloseResourceFork();
else
err = fBackingFile->CloseResourceFork();
if ( err != noErr )
goto cleanup;
// "clean" this document by resetting the WE instance modification count
WEResetModCount( fWEHandle );
err = noErr;
// clean up
cleanup:
// remember, don't dispose the hText handle!
if (hStyles)
DisposeHandle( (Handle) hStyles );
if (hSoup)
DisposeHandle( (Handle) hSoup );
if ( replacing )
{
// since we were replacing an existing file, let's now swap the original
// and the temp file. let's hear it for safe saves.
err = FSpExchangeFiles( &tempFileSpec, &filespec );
if ( err != noErr )
return err;
// can the temp file since we don't need it anymore
tempFile->SetSpecifier(tempFileSpec);
err = tempFile->Delete();
if ( err != noErr )
return err;
delete tempFile;
}
// and update the disk with any unwritten data
FlushVol( "\p", filespec.vRefNum );
return err;
}
static CommandID SelectedColor(RGBColor& theColor)
{
if (theColor.red == 0x0000 && theColor.green == 0x0000 && theColor.blue == 0x0000)
return cBlack;
if (theColor.red == 42253 && theColor.green == 30947 && theColor.blue == 0x0000)
return cBrown;
if (theColor.red == 0x0000 && theColor.green == 0x0000 && theColor.blue == 54272)
return cBlue;
if (theColor.red == 0x0000 && theColor.green == 32768 && theColor.blue == 4528)
return cGreen;
if (theColor.red == 56683 && theColor.green == 2242 && theColor.blue == 1698)
return cRed;
if (theColor.red == 65535 && theColor.green == 0x0000 && theColor.blue == 52428)
return cPink;
if (theColor.red == 65535 && theColor.green == 27594 && theColor.blue == 0x0000)
return cOrange;
return cOtherColor;
}